#include <iostream>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>

using namespace std;

// 全局变量可以被多个线程看到。
int g_val = 100;

// 可以被多个执行流同时执行，show函数被重入了。
void show(const string& name)
{
    cout << name << "say# " << "hello world" << endl;
}

void* threadRoutine(void* args)
{
    const char* name = (const char*)args;
    int cnt = 5;
    while(true)
    {
        // printf("new thread pid: %d, g_val: %d, &g_val:%p\n", getpid(), g_val, &g_val);
        printf("%s pid: %d, g_val: %d, &g_val:%p\n", name, getpid(), g_val, &g_val);
        // cout << "new thread: " << getpid() << endl;
        // show("[new thread]");
        sleep(1);

        // 任何一个线程如果出现除零，野指针问题导致线程崩溃，进程也会随着崩溃。
        // sleep(5);
        // int a = 10;
        // a /= 0;

        cnt--;
        if(cnt == 0) break;
    }

    pthread_exit((void*)100);
    // exit(11);      // 直接调用exit,exit是用来终止进程的。不能来终止线程。
    // return nullptr; // 走到这里，这个线程还会回到主线程继续往后执行吗？不会，走到这里新线程退出。
    // return (void*)100;   // 把1拷贝到void*变量里边。
}

int main()
{
    // 线程id是一个虚拟地址。也是在库当中对应的线程结构体的起始地址。
    pthread_t tid;   // 输出型参数,线程id

    // 第一个参数传入pthread_t的类型，表示线程id
    // 第二个参数代表线程的属性，比如线程的栈大小。一般设置为nullptr
    // 第三个参数是函数指针类型，返回值是void*,参数也是void*。要传入一个函数，本质是回调函数。是新线程的入口函数
    // 第四个参数是输入性参数。是线程创建成功，新线程回调函数的时候，需要传递的参数。
    // pthread_create函数创建线程成功，返回值是0.如果失败，返回错误码，没有errno.
    // pthread_create(&tid, nullptr, threadRoutine, nullptr);  // 不是系统调用
    pthread_create(&tid, nullptr, threadRoutine, (void*)"Thread 1"); 


    sleep(1);
    // 让主线程取消新线程。但是一定是新线程已经起来了。
    pthread_cancel(tid);

    // while(true)
    // {
    //     // tid以整数形式打印出来的值很大，不是我们认为的LWP对应的值。因为lwp是操作系统范畴的，操作系统知道就可以。而tid以
    //     // 地址的形式打印出来才是给用户使用的。
    //     printf("main thread pid: %d, g_val: %d, &g_val:%p， create new thread tid: %ul\n", getpid(), g_val, &g_val, tid);
    //     // cout << "main thread: " << getpid() << ", g_val: " << g_val << ", &g_val" << &g_val << endl;
    //     // show("[main thread]");
    //     sleep(1); 
    //     g_val++;
    // }

    // pthread_join(tid, nullptr);   // main thread等待的时候，默认是阻塞等待的。

    void* ret;
    pthread_join(tid, &ret);

    // 这里强转的时候会报错，因为指针是8个字节，而整数是4个字节。需要强转成long long
    // cout << "ret: " << ret << ", " << (int)ret << endl;
    cout << "ret: " << ret << ", " << (long long)ret << endl;
    cout << "main thread quit ..." << endl;
    return 0;
}

// 任何一个线程被杀死，整个进程也就没了。这就是线程健壮性很低的原因。


// 一个线程被创建出来，是主线程先跑，还是新线程先跑呢？ 不确定，调度器说了算。但是主线程应该是最后一个退出。pthread_join能保证主线程后退。
// 因为是主线程创建的新线程，本质就是对线程做管理。主线程退出谁来管理新线程呢？
// 如何知道新线程执行完，执行的结果怎么样？是通过返回值来让我们知道。那么主线程如何得到新线程的返回值呢？

// 线程等待的两目的：
// 1、防止新线程造成内存泄露
// 2、得到新线程的退出结果

// 线程如何终止
// 1、直接在线程函数中return
// 2、pthread_exit函数
// 3、线程取消函数pthread_cancel
// 4、如果一个线程是被取消的，那么该线程的退出结果是PTHREAD_CANCELED这个宏，值是-1。
// PTHREAD_CANCELED;